package cn.itcast.nems.common.util;

import org.springframework.util.StringUtils;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.TemporalUnit;
import java.time.temporal.WeekFields;
import java.util.Date;
import java.util.LinkedList;
import java.util.Locale;
import java.util.stream.Stream;

/**
 * LocalDateTimeUtils Java8中的时间类 线程安全
 *
 * @author: renyu
 * @create: 2019-06-25 15:35
 */
public class LocalDateTimeUtils {
    private static final String DEFAULT_LOCAL_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    private static final String DEFAULT_LOCAL_DATE_FORMAT = "yyyy-MM-dd";
    private static final String DEFAULT_LOCAL_TIME_FORMAT = "HH:mm:ss";
    public static final String SINICIZATION_LOCAL_DATE_TIME_FORMAT = "yyyy年MM月dd日 HH时mm分ss秒";
    /**
     * 一天的毫秒数。
     */
    public final static long DAY_MILLISECODE = 24 * 60 * 60 * 1000;

    /**
     * 计算服务天数，endDate - startDate天数+1。
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 结束-开始日期天数+1
     */
    public static int calServiceDays(LocalDate startDate, LocalDate endDate) {
        final long days = ChronoUnit.DAYS.between(startDate, endDate) + 1;
        return (int) days;
    }

    /**
     * Date转换为LocalDateTime
     *
     * @param date
     * @return LocalDateTime
     */
    public static LocalDateTime convertDateToLDT(Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
    }

    /**
     * LocalDateTime转换为Date
     *
     * @param time
     * @return
     */
    public static Date convertLDTToDate(LocalDateTime time) {
        return Date.from(time.atZone(ZoneId.systemDefault()).toInstant());
    }


    /**
     * 获取指定日期的毫秒
     *
     * @param time
     * @return
     */
    public static Long getMilliByTime(LocalDateTime time) {
        return time.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
    }

    /**
     * 获取指定日期的秒
     *
     * @param time
     * @return
     */
    public static Long getSecondsByTime(LocalDateTime time) {
        return time.atZone(ZoneId.systemDefault()).toInstant().getEpochSecond();
    }

    /**
     * 将long类型的timestamp转为LocalDateTime
     *
     * @param timestamp
     * @return
     */
    public static LocalDateTime getLocalDateTimeOfTimestamp(Long timestamp) {
        if (timestamp == null || timestamp == 0) {
            return null;
        }
        Instant instant = Instant.ofEpochMilli(timestamp);
        ZoneId zone = ZoneId.systemDefault();
        return LocalDateTime.ofInstant(instant, zone);
    }

    /**
     * 获取一年中的第几个星期
     *
     * @param localDate
     * @return
     */
    public static int getWeekOfYear(LocalDate localDate) {
        WeekFields weekFields = WeekFields.of(DayOfWeek.MONDAY, 1);
        return localDate.get(weekFields.weekOfYear());
    }

    /**
     * 获取星期几  星期一为1  星期日为7
     *
     * @param dateTime
     * @return
     */
    public static int getWeek(LocalDateTime dateTime) {
        return dateTime.get(WeekFields.of(DayOfWeek.of(1), 1).dayOfWeek());
    }

    /**
     * 获取星期几  星期一为1  星期日为7
     *
     * @param date
     * @return
     */
    public static int getWeek(LocalDate date) {
        return date.get(WeekFields.of(DayOfWeek.of(1), 1).dayOfWeek());
    }

    /**
     * 获取指定时间的指定格式
     *
     * @param time
     * @return
     */
    public static String formatLocalDateTime(LocalDateTime time) {
        return formatLocalDateTime(time, DEFAULT_LOCAL_DATE_TIME_FORMAT);
    }

    /**
     * 获取指定时间的指定格式
     *
     * @param time
     * @param pattern
     * @return
     */
    public static String formatLocalDateTime(LocalDateTime time, String pattern) {
        return time.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 格式化LocalDate 指定格式
     *
     * @param localDate
     * @param pattern
     * @return
     */
    public static String formatLocalDate(LocalDate localDate, String pattern) {
        return localDate.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 格式化LocalDate 格式为yyyy-MM-dd
     *
     * @param localDate
     * @return
     */
    public static String formatLocalDate(LocalDate localDate) {
        return localDate.format(DateTimeFormatter.ofPattern(DEFAULT_LOCAL_DATE_FORMAT));
    }

    /**
     * 格式化LocalTime 指定格式
     *
     * @param localTime
     * @param pattern
     * @return
     */
    public static String formatLocalTime(LocalTime localTime, String pattern) {
        return localTime.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 格式化LocalTime 格式为 HH:mm:ss
     *
     * @param localTime
     * @return
     */
    public static String formatLocalTime(LocalTime localTime) {
        return localTime.format(DateTimeFormatter.ofPattern(DEFAULT_LOCAL_TIME_FORMAT));
    }

    /**
     * 获取当前时间的指定格式
     *
     * @param pattern
     * @return
     */
    public static String formatNow(String pattern) {
        return formatLocalDateTime(LocalDateTime.now(), pattern);
    }

    /**
     * 获取当前时间 格式为：yyyy-MM-dd HH:mm:ss
     *
     * @return
     */
    public static String nowString() {
        return formatLocalDateTime(LocalDateTime.now(), DEFAULT_LOCAL_DATE_TIME_FORMAT);
    }

    /**
     * 日期加上一个数
     *
     * @param time
     * @param number 添加数量
     * @param field  单位 参数为ChronoUnit.* (年 月 日 时 分 秒)
     * @return
     */
    public static LocalDateTime plus(LocalDateTime time, long number, TemporalUnit field) {
        return time.plus(number, field);
    }

    /**
     * 日期减去一个数
     *
     * @param time
     * @param number 减去数量
     * @param field  单位 参数为ChronoUnit.* (年 月 日 时 分 秒)
     * @return
     */
    public static LocalDateTime minu(LocalDateTime time, long number, TemporalUnit field) {
        return time.minus(number, field);
    }

    /**
     * 获取两个日期的差
     *
     * @param startTime
     * @param endTime
     * @param field     单位 参数为ChronoUnit.* (年 月 日 时 分 秒)
     * @return
     */
    public static long betweenTwoTime(LocalDateTime startTime, LocalDateTime endTime, ChronoUnit field) {
        Period period = Period.between(LocalDate.from(startTime), LocalDate.from(endTime));
        if (field == ChronoUnit.YEARS) {
            return period.getYears();
        }
        if (field == ChronoUnit.MONTHS) {
            return period.getYears() * 12 + period.getMonths();
        }
        return field.between(startTime, endTime);
    }

    /**
     * 字符串格式化成LocalDateTime
     *
     * @param dateString 时间字符串
     * @param pattern    转换格式
     * @return
     */
    public static LocalDateTime parse(String dateString, String pattern) {
        if (null == dateString || "".equals(dateString)) {
            return null;
        }
        DateTimeFormatter df = DateTimeFormatter.ofPattern(pattern);
        return LocalDateTime.parse(dateString, df);
    }

    /**
     * 字符串格式化成LocalDateTime
     *
     * @param dateString 时间字符串
     * @return
     */
    public static LocalDateTime parse(String dateString) {
        return parse(dateString, DEFAULT_LOCAL_DATE_TIME_FORMAT);
    }

    /**
     * 字符串格式换localDate
     *
     * @param dateString
     * @param pattern
     * @return
     */
    public static LocalDate parseLocalDate(String dateString, String pattern) {
        if (!StringUtils.hasText(dateString)) {
            return null;
        }
        DateTimeFormatter df = DateTimeFormatter.ofPattern(pattern);
        return LocalDate.parse(dateString, df);
    }

    /**
     * 字符串格式换localDate 默认格式 yyyy-MM-dd
     *
     * @param dateString
     * @return
     */
    public static LocalDate parseLocalDate(String dateString) {
        if (!StringUtils.hasText(dateString)) {
            return null;
        }
        return parseLocalDate(dateString, DEFAULT_LOCAL_DATE_FORMAT);
    }

    /**
     * 字符串格式换 LocalTime
     *
     * @param dateString
     * @param pattern
     * @return
     */
    public static LocalTime parseLocalTime(String dateString, String pattern) {
        DateTimeFormatter df = DateTimeFormatter.ofPattern(pattern);
        return LocalTime.parse(dateString, df);
    }

    /**
     * 字符串格式换 LocalTime 默认格式 HH:mm:ss
     *
     * @param dateString
     * @return
     */
    public static LocalTime parseLocalTime(String dateString) {
        return parseLocalTime(dateString, DEFAULT_LOCAL_TIME_FORMAT);
    }

    /**
     * 返回出两个日期中间天数列表 格式 yyyy-MM-dd
     *
     * @param startTime
     * @param endTime
     * @return
     */
    public static LinkedList<String> betweenTwoTimeDayList(String startTime, String endTime) {

        LinkedList<String> list = new LinkedList<>();
        LocalDate startDate = LocalDate.parse(startTime);
        LocalDate endDate = LocalDate.parse(endTime);

        long distance = ChronoUnit.DAYS.between(startDate, endDate);
        if (distance < 0) {
            return list;
        } else if (distance == 0) {
            if (startTime.equals(endTime)) {
                list.add(endTime);
            }
            return list;
        }
        Stream.iterate(startDate, d -> {
            return d.plusDays(1);
        }).limit(distance + 1).forEach(f -> {
            list.add(f.toString());
        });
        return list;
    }

    /**
     * 根据一年中的第几个星期 获取这个星期内天数的时间list
     *
     * @param weekOfYear
     * @return
     */
    public static LinkedList<String> getDayListByWeekOfYear(int weekOfYear) {
        WeekFields weekFields = WeekFields.of(Locale.getDefault());
        int year = LocalDate.now().getYear();
        if (weekOfYear / 53 > 0) {
            year += weekOfYear / 53;
            weekOfYear = weekOfYear % 53;
        }

        LocalDate first = LocalDate.now()
                .withYear(year)
                .with(weekFields.weekOfYear(), weekOfYear)
                .with(weekFields.dayOfWeek(), 2);

        LocalDate end = LocalDate.now()
                .withYear(year)
                .with(weekFields.weekOfYear(), weekOfYear + 1)
                .with(weekFields.dayOfWeek(), 1);
        return betweenTwoTimeDayList(formatLocalDate(first), formatLocalDate(end));
    }

    /**
     * 获取日期内星期一的日期
     *
     * @param localDate
     * @return
     */
    public static LocalDate getMonday(LocalDate localDate) {
        return localDate.with(DayOfWeek.MONDAY);
    }

    /**
     * 获取日期内星期日的日期
     *
     * @param localDate
     * @return
     */
    public static LocalDate getSunday(LocalDate localDate) {
        return localDate.with(DayOfWeek.SUNDAY);
    }

    /**
     * 获取指定日期所属月份的最后一天
     *
     * @param localDate
     * @return
     */
    public static LocalDate getLastDayOfMonth(LocalDate localDate) {
        return localDate.with(TemporalAdjusters.lastDayOfMonth());
    }

    /**
     * 获取指定日期所属月份的第一天
     *
     * @param localDate
     * @return
     */
    public static LocalDate getFirstDayOfMonth(LocalDate localDate) {
        return localDate.with(TemporalAdjusters.firstDayOfMonth());
    }

    /**
     * 秒转时间字符串 00:00:00
     *
     * @param time
     * @return
     */
    public static String secToTime(int time) {
        String timeStr = null;
        int hour = 0;
        int minute = 0;
        int second = 0;
        if (time <= 0)
            return "00:00:00";
        else {
            minute = time / 60;
            if (minute < 60) {
                second = time % 60;
                timeStr = "00:" + unitFormat(minute) + ":" + unitFormat(second);
            } else {
                hour = minute / 60;
                if (hour > 99)
                    return "99:59:59";
                minute = minute % 60;
                second = time - hour * 3600 - minute * 60;
                timeStr = unitFormat(hour) + ":" + unitFormat(minute) + ":" + unitFormat(second);
            }
        }
        return timeStr;
    }

    public static String unitFormat(int i) {
        String retStr = null;
        if (i >= 0 && i < 10)
            retStr = "0" + Integer.toString(i);
        else
            retStr = "" + i;
        return retStr;
    }

    /**
     * 计算当前时间是否在给定时间的一天变化范围内。<br>
     * (millisecond-DAY_MILLISECODE) <= currentMillisecond <=
     * (millisecond+DAY_MILLISECODE)
     *
     * @param millisecond
     * @return
     */
    public static boolean isCurrentTimeInDayRange(long millisecond) {
        return isCurrentTimeInRange(millisecond, DAY_MILLISECODE);
    }

    /**
     * 计算当前时间是否在给定时间的变化范围内。<br>
     * (millisecond-delta) <= currentMillisecond <= (millisecond+delta)
     *
     * @param millisecond
     * @param delta
     * @return
     */
    public static boolean isCurrentTimeInRange(long millisecond, long delta) {
        if (millisecond < 0 || delta < 0) {
            return false;
        }
        long sum = millisecond + delta;
        if (sum < 0) {
            return false;
        }
        long currentMillisecond = System.currentTimeMillis();
        boolean b = sum >= currentMillisecond;
        if (b) {
            long sub = millisecond - delta;
            b = sub <= currentMillisecond;
        }
        return b;
    }

    /**
     * 获取当前月份
     *
     * @return 当前月份数字
     */
    public static int currentMonth() {
        return LocalDate.now().getMonthValue();
    }

    /**
     * 时间戳转化为LocalDateTime
     *
     * @param timestamp 时间戳
     * @return LocalDateTime
     */
    public static LocalDateTime convertTimestamp(long timestamp) {
        return LocalDateTime.ofEpochSecond(timestamp / 1000, 0, ZoneOffset.ofHours(8));
    }

    public static void main(String[] args) {
        System.out.println(LocalDateTimeUtils.betweenTwoTime(LocalDateTime.of(2019, 11, 18, 11, 1, 1)
                , LocalDateTime.of(2019, 11, 18, 11, 1, 2), ChronoUnit.SECONDS));
        System.out.println(currentMonth());
        System.out.println(formatLocalDateTime(convertTimestamp(System.currentTimeMillis())));
        System.out.println(formatLocalTime(LocalDateTime.now().toLocalTime(), "HH:mm"));
    }

}